package com.qd.tfpay.order;

import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.cdqidi.exception.ApiException;
import com.cdqidi.httpclientbootstart.util.HttpUtil;
import com.cdqidi.util.DateUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qd.pay.config.PayInfoConfig;
import com.qd.tfpay.util.SignUtil;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 天府银行聚合支付
 *
 * @author sjk
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class OrderApi {

    private static final String FRONT_PAY_URL = "/tianfupay/pay/frontPayV3";
    private static final String FRONT_REFUND_URL = "/tianfupay/pay/frontRefund";
    private static final String QUERY_ORDER_URL = "/tianfupay/query/queryOrder";
    private static final String DOWNLOAD_ACCOUNT_URL = "/tianfupay/mag/downloadAccount";
    private static final String CLOSE_ORDER_URL = "/tianfupay/trans/closeOrder";
    private static final String QUERY_BALANCEBY_VACCOUNTNO_URL = "/tianfupay/trans/queryBalanceByVaccountNo";
    private static final String SERVICE_MSG = "service";
    private static final String SERIAL_NO_MSG = "serialno";
    private static final String OUT_TRADE_NO_MSG = "out_trade_no";
    private static final String PARTNER_MSG = "partner";
    private static final String SUB_PARTNER_MSG = "subpartner";
    private static final String PRINT = "url: {}";
    private final PayInfoConfig config;
    private final HttpUtil httpUtil;
    private final ObjectMapper objectMapper;

    /**
     * 创建订单
     *
     * @param params 请求参数
     */
    @SneakyThrows
    public BaseResponseBO createOrder(Map<String, Object> params) {
        params.put(SERVICE_MSG, "pay_service");
        Map<String, String> headers = buildHead(params);
        headers.put(SERIAL_NO_MSG, params.getOrDefault(OUT_TRADE_NO_MSG, IdWorker.getId()).toString());
        String reqJsonStr = SignUtil.createReqJsonStr(params, config.getTfBankApiKey());
        if (log.isDebugEnabled()) {
            log.debug("创建订单,请求参数:{},请求头:{}", reqJsonStr, headers);
            log.debug(PRINT, config.getTfBankDomain() + FRONT_PAY_URL);
        }
        String s = httpUtil.sendHttpsPost(config.getTfBankDomain() + FRONT_PAY_URL, reqJsonStr, headers);
        if (log.isDebugEnabled()) {
            log.debug("createOrder response:{}", s);
        }
        return getResponse(s);
    }

    /**
     * 关闭订单
     *
     * @param partner    服务商号,由天府支付统一分配
     * @param subPartner 商户号，由天府支付统一分配
     * @param outTradeNo 商户系统订单号，支付流水号与商户订单号不能同时为空
     */
    @SneakyThrows
    public BaseResponseBO closeOrder(String partner, String subPartner, String outTradeNo) {
        Map<String, Object> params = new HashMap<>(16);
        params.put(SERVICE_MSG, "close_order");
        params.put(PARTNER_MSG, partner);
        params.put(SUB_PARTNER_MSG, subPartner);
        params.put(OUT_TRADE_NO_MSG, outTradeNo);
        Map<String, String> headers = buildHead(params);
        headers.put(SERIAL_NO_MSG, params.getOrDefault(OUT_TRADE_NO_MSG, IdWorker.getId()).toString());
        String reqJsonStr = SignUtil.createReqJsonStr(params, config.getTfBankApiKey());
        if (log.isDebugEnabled()) {
            log.debug("关闭订单,请求参数:{},请求头:{}", reqJsonStr, headers);
            log.debug(PRINT, config.getTfBankDomain() + CLOSE_ORDER_URL);
        }
        String s = httpUtil.sendHttpsPost(config.getTfBankDomain() + CLOSE_ORDER_URL, reqJsonStr, headers);
        if (log.isDebugEnabled()) {
            log.debug("closeOrder response:{}", s);
        }
        return getResponse(s);
    }


    /**
     * 退款
     *
     * @param params 请求参数
     */
    @SneakyThrows
    public BaseResponseBO refundOrder(Map<String, Object> params) {
        params.put(SERVICE_MSG, "refund_service");
        Map<String, String> headers = buildHead(params);
        headers.put(SERIAL_NO_MSG, params.getOrDefault(OUT_TRADE_NO_MSG, IdWorker.getId()).toString());
        String reqJsonStr = SignUtil.createReqJsonStr(params, config.getTfBankApiKey());
        if (log.isDebugEnabled()) {
            log.debug("退款,请求参数:{},请求头:{}", reqJsonStr, headers);
            log.debug(PRINT, config.getTfBankDomain() + FRONT_REFUND_URL);
        }
        String s = httpUtil.sendHttpsPost(config.getTfBankDomain() + FRONT_REFUND_URL, reqJsonStr, headers);
        if (log.isDebugEnabled()) {
            log.debug("refundOrder response:{}", s);
        }
        return getResponse(s);
    }

    /**
     * 订单查询
     *
     * @param partner    服务商号,由天府支付统一分配
     * @param subPartner 商户号，由天府支付统一分配
     * @param outTradeNo 商户系统内部的订单号,32个字符内、可包含字母,确保在商户系统唯一。（退款交易查询时，则是退款发起交易的退款单号）
     */
    @SneakyThrows
    public BaseResponseBO queryOrder(String partner, String subPartner, String outTradeNo) {
        Map<String, Object> params = new HashMap<>(16);
        params.put(SERVICE_MSG, "close_order");
        params.put(PARTNER_MSG, partner);
        params.put(SUB_PARTNER_MSG, subPartner);
        params.put(OUT_TRADE_NO_MSG, outTradeNo);
        Map<String, String> headers = buildHead(params);
        headers.put(SERIAL_NO_MSG, outTradeNo);
        String reqJsonStr = SignUtil.createReqJsonStr(params, config.getTfBankApiKey());
        if (log.isDebugEnabled()) {
            log.debug("订单查询,请求参数:{},请求头:{}", reqJsonStr, headers);
            log.debug(PRINT, config.getTfBankDomain() + QUERY_ORDER_URL);
        }
        String s = httpUtil.sendHttpsPost(config.getTfBankDomain() + QUERY_ORDER_URL, reqJsonStr, headers);
        if (log.isDebugEnabled()) {
            log.debug("queryOrder response:{}", s);
        }
        return getResponse(s);

    }

    /**
     * 下载对账文件，默认下载昨天的，下载全部
     *
     * @param partner 服务商号,由天府支付统一分配的(120XXXXXXX)
     * @return 对账单信息
     */
    public AccountBO downloadAccount(String partner) {
        String orderDate = DateUtil.localDateToString(LocalDate.now().plusDays(-1), "yyyyMMdd");
        return downloadAccount(partner, orderDate);
    }

    /**
     * 下载对账文件，默认下载昨天的，下载全部
     *
     * @param partner 服务商号,由天府支付统一分配的(120XXXXXXX)
     * @return 对账单信息
     */
    public AccountBO downloadAccount(String partner, String orderDate) {
        return downloadAccount(partner, null, orderDate, "0001");
    }

//    /**
//     * 下载对账文件，默认下载昨天的，下载全部
//     *
//     * @param partner    服务商号,由天府支付统一分配的(120XXXXXXX)
//     * @param subPartner 特约商户号,由天府支付统一分配的。特约商户必须送商户号和特约商户号。
//     * @return 对账单信息
//     */
//    public AccountBO downloadAccount(String partner, String subPartner) {
//        String orderDate = DateUtil.localDateToString(LocalDate.now().plusDays(-1), "yyyyMMdd");
//        String orderType = "0001";
//        return downloadAccount(partner, subPartner, orderDate, orderType);
//    }

    /**
     * 下载对账文件
     *
     * @param partner    服务商号,由天府支付统一分配的(120XXXXXXX)
     * @param subPartner 特约商户号,由天府支付统一分配的。特约商户必须送商户号和特约商户号。
     * @param orderDate  下载对账单的日期，格式： yyyyMMdd    例如：20130910
     * @param orderType  下载对账单类型
     *                   0001：返回当日所有成功的订单（包含支付成功，退款成功，预授权成功的订单）
     *                   0002：返回当日成功支付的订单
     *                   0003：返回当日退款的订单
     *                   0004:  返回当日解冻
     *                   0009：返回当日订单分账订单
     *                   0010：返回当日订单分账退回订单
     *                   默认：0001；
     * @return 对账单信息
     */
    @SneakyThrows
    public AccountBO downloadAccount(String partner, String subPartner, String orderDate, String orderType) {
        Map<String, Object> params = new HashMap<>(16);
        params.put("service_version", "3.0");
        params.put(SERVICE_MSG, "account_download_service");
        params.put(PARTNER_MSG, partner);
        if (StringUtils.hasLength(subPartner)) {
            params.put(SUB_PARTNER_MSG, subPartner);
        }
        params.put("order_date", orderDate);
        params.put("order_type", orderType);
        Map<String, String> headers = buildHead(params);
        headers.remove(PARTNER_MSG);
        String reqJsonStr = SignUtil.createReqJsonStr(params, config.getTfBankApiKey());
        log.debug("下载对账单,请求参数:{},请求头:{}", reqJsonStr, headers);
        log.debug(PRINT, config.getTfBankDomain() + DOWNLOAD_ACCOUNT_URL);
        String s = httpUtil.sendHttpsPost(config.getTfBankDomain() + DOWNLOAD_ACCOUNT_URL, reqJsonStr, headers);
        log.debug("downloadAccount response:{}", s);
        return objectMapper.readValue(s, AccountBO.class);
    }

    public BaseResponseBO getResponse(String s) throws JsonProcessingException {
        JsonNode jn = new ObjectMapper().readTree(s);
        String retCode = Optional.ofNullable(jn.get("retCode")).orElseThrow(() -> new ApiException("响应码为空")).asText();
        String retMsg = Optional.ofNullable(jn.get("retMsg")).orElseThrow(() -> new ApiException("响应消息为空")).asText();
        String returnSerNo = Optional.ofNullable(jn.get("returnSerNo")).orElseThrow(() -> new ApiException("响应序列号为空")).toString();
        BaseResponseBO response = new BaseResponseBO();
        response.setRetCode(retCode)
                .setRetMsg(retMsg)
                .setOriginalMessage(objectMapper.readTree(s))
                .setReturnSerNo(returnSerNo);
        if (retCode.equals("0000")) {
            String service = Optional.ofNullable(jn.get(SERVICE_MSG)).orElseThrow(() -> new ApiException("接口名称为空")).asText();
            String serviceVersion = Optional.ofNullable(jn.get("service_version")).orElseThrow(() -> new ApiException("接口版本为空")).toString();
            String inputCharset = Optional.ofNullable(jn.get("input_charset")).orElseThrow(() -> new ApiException("字符集为空")).toString();
            String signType = Optional.ofNullable(jn.get("sign_type")).orElseThrow(() -> new ApiException("签名方式为空")).toString();
            String sign = Optional.ofNullable(jn.get("sign")).orElseThrow(() -> new ApiException("签名为空")).toString();
            response.setService(service)
                    .setServiceVersion(serviceVersion)
                    .setInputCharset(inputCharset)
                    .setSignType(signType)
                    .setSign(sign);
        }
        return response;
    }

    public Map<String, String> buildHead(Map<String, Object> params) {
        Map<String, String> header = new HashMap<>();
        header.put("client", config.getDomain());
        header.put("senddtt", DateUtil.localDateTimeToString(LocalDateTime.now(), "yyyyMMddHHmmss"));
        header.put(PARTNER_MSG, params.get(PARTNER_MSG).toString());
        final Object subPartner = params.getOrDefault(SUB_PARTNER_MSG, "");
        if (null != subPartner) {
            header.put(SUB_PARTNER_MSG, subPartner.toString());
        }
        return header;
    }

    /**
     * 账户余额查询接口
     *
     * @param params 请求参数
     */
    @SneakyThrows
    public BaseResponseBO queryBalanceByVaccountNo(Map<String, Object> params) {
        params.put(SERVICE_MSG, "trans_queryBalanceByVaccountNo");
        Map<String, String> headers = buildHead(params);
        headers.put(SERIAL_NO_MSG, params.getOrDefault(OUT_TRADE_NO_MSG, IdWorker.getId()).toString());
        String reqJsonStr = SignUtil.createReqJsonStr(params, config.getTfBankApiKey());
        if (log.isDebugEnabled()) {
            log.debug("账户余额查询接口,请求参数:{},请求头:{}", reqJsonStr, headers);
            log.debug(PRINT, config.getTfBankDomain() + QUERY_BALANCEBY_VACCOUNTNO_URL);
        }
        String s = httpUtil.sendHttpsPost(config.getTfBankDomain() + QUERY_BALANCEBY_VACCOUNTNO_URL, reqJsonStr, headers);
        if (log.isDebugEnabled()) {
            log.debug("账户余额查询接口,response:{}", s);
        }
        return getResponse(s);
    }

}